package com.tungse.study.mixed_model;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * @Description:  计算器
 * @Author: Tungse
 */
public class Calc {
    public Double calculate(String exp) {
        CalcExpression calcExpression = new CalcExpression();
        LinkedList<String> suffix = calcExpression.toSuffix(exp);
        Expression expression = calcExpression.suffixExpressionCalc(suffix);
        return expression.getValue();
    }


    static class CalcExpression {
        //1 中缀表达式 转 后缀表达式
        /* 中缀表达式转后缀表达式式逻辑
         * 操作顺序‌：从左到右扫描中缀表达式。
         *  数字‌：直接输出到后缀表达式。
         *  ‌左括号‌：压入栈。
         *  ‌右括号‌：弹出栈顶元素并输出，直到遇到左括号（左括号弹出但不输出）。
         *  ‌运算符‌：
         *   如果栈为空，直接入栈。
         *   如果栈顶运算符的优先级大于等于当前运算符的优先级，则弹出栈顶运算符，并将当前运算符入栈。
         *  结束处理‌：将栈中剩余元素依次弹出并输出。
         */
        public LinkedList<String> toSuffix(String exp) {
            LinkedList<String> stackList = new LinkedList<>();
            LinkedList<String> expList = new LinkedList<>();
            for (int i = 0; i < exp.length(); i++) {
                char c = exp.charAt(i);
                //数字‌：直接输出到后缀表达式。
                if (Character.isDigit(c)) {
                    StringBuilder numStr = new StringBuilder();
                    while (Character.isDigit(c)) {
                        numStr.append(c);
                        i++;
                        if (i == exp.length()) break;
                        c = exp.charAt(i);
                    }
                    expList.add(numStr.toString());
                    i--;
                    //左括号‌：压入栈。
                } else if (c == '(') {
                    stackList.addLast(c + "");
                    //右括号‌：弹出栈顶元素并输出，直到遇到左括号（左括号弹出但不输出）。
                } else if (c == ')') {
                    while (!"(".equals(stackList.getLast())) {
                        expList.add(stackList.removeLast());
                    }
                    stackList.removeLast();
                } else if (c == '*' || c == '/') {
                    while ((!stackList.isEmpty() && (stackList.getLast().equals("*") || stackList.getLast().equals("/")))) {
                        expList.add(stackList.removeLast());
                    }
                    stackList.addLast(c + "");
                } else if (c == '+' || c == '-') {
                    while (topIfOperation(stackList)) {
                        expList.add(stackList.removeLast());
                    }
                    stackList.addLast(c + "");
                } else {
                    throw new RuntimeException("表达式错误");
                }
            }
            while (!stackList.isEmpty()) {
                expList.add(stackList.removeLast());
            }
            return expList;
        }

        //2 后缀表达式计算

        /**
         * ‌扫描表达式‌：从左至右扫描后缀表达式。
         * ‌压入数字‌：遇到数字时，将其压入堆栈。
         * ‌弹出和计算‌：遇到运算符时，弹出栈顶的两个数（先出栈的是右操作数，后出栈的是左操作数），用运算符对它们进行计算，并将结果入栈。
         * ‌重复过程‌：重复上述步骤直到表达式最右端，最后运算得出的值即为表达式的结果
         * @param expList
         * @return
         */
        public Expression suffixExpressionCalc(List<String> expList) {
            LinkedList<Expression> stakcExpression = new LinkedList<>();
            Expression right = null;
            for (String s : expList) {
                switch (s)  {
                    case "+":
                        right = stakcExpression.removeLast();
                        stakcExpression.addLast(new AddExpression(stakcExpression.removeLast(), right));
                        break;
                    case "-":
                        right = stakcExpression.removeLast();
                        stakcExpression.addLast(new SubExpression(stakcExpression.removeLast(), right));
                        break;
                    case "*":
                        right = stakcExpression.removeLast();
                        stakcExpression.addLast(new MultExpression(stakcExpression.removeLast(), right));
                        break;
                    case "/":
                        right = stakcExpression.removeLast();
                        stakcExpression.addLast(new DivExpression(stakcExpression.removeLast(), right));
                        break;
                    default:
                        stakcExpression.addLast(new NumberExpression(Double.parseDouble(s)));
                }
            }
            return stakcExpression.removeLast();
        }

        public Boolean topIfOperation(LinkedList<String> stackList) {
            if (stackList.isEmpty()) {
                return false;
            } else {
                return Set.of("+","-","*","/").contains(stackList.getLast());
            }
        }
    }

}

